package com.zgm.dao.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

import javax.annotation.Resource;

import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.springframework.stereotype.Repository;

import com.zgm.bean.Pager;
import com.zgm.dao.BaseDao;

@Repository
public class BaseDaoImpl<T, PK extends Serializable> implements BaseDao<T, PK> {

	@Resource
	private SessionFactory sessionFactory;
	private Class<T> entityClass;
	
	public BaseDaoImpl() {
		Class c = super.getClass();
		Type type = c.getGenericSuperclass();
		
		if ( type instanceof ParameterizedType ) {
			Type[] parameterizedTypes = ( ( ParameterizedType ) type ).getActualTypeArguments();
			entityClass = ( Class<T> ) parameterizedTypes[0];
		}
	}
	
	public T get(PK pk) {
		return ( T ) this.getSession().get( entityClass, pk );
	}

	public T load(PK pk) {
		T t = ( T ) this.getSession().load(  entityClass, pk );
		
		return t;
	}
	
	public T loadInitialize(PK pk) {
		T t = ( T ) this.getSession().load(  entityClass, pk );
		Hibernate.initialize( t );
		
		return t;
	}

	public T save(T t) {
		this.getSession().save( t );
		return t;
	}
	
	public Session getSession() {
		return this.sessionFactory.getCurrentSession();
	}

	@Override
	public void update(T t) {
		this.getSession().update( t );
	}

	@Override
	public void delete(T t) {
		this.getSession().delete( t );
	}

	@Override
	public void merge(T t) {
		this.getSession().merge( t );
	}

	@Override
	public void flush() {
		this.getSession().flush();
	}

	@Override
	public Long getTotalCount() {
		String hql = "select count(*) from " + entityClass.getName();
		
		return ( Long ) this.getSession().createQuery( hql ).uniqueResult();
	}

	@Override
	public List<T> getAllList() {
		String hql = "from " + entityClass.getName();
		
		return this.getSession().createQuery( hql ).list();
	}

	public List<T> getList2(String propertyName, String propertyValue) {
		String hql = "from " + entityClass.getName() + " where " + propertyName + " = '" + propertyValue + "'";
		
		return this.getSession().createQuery( hql ).list();
	}
	
	public List<T> getList(String propertyName, Object propertyValue) {
//		String hql = "from " + entityClass.getName() + " as model where model." + propertyName + " = ?" ;
//		
//		return this.getSession().createQuery( hql ).setParameter( 0, propertyValue ).list();
		
		String hql = "from " + entityClass.getName() + " as model where model." + propertyName + " = :propertyValue" ;
		
		return this.getSession().createQuery( hql ).setParameter( "propertyValue", propertyValue ).list();
	}

	@Override
	public Pager findPager(Pager pager) {
		String hql = "from " + entityClass.getName() + " as entity ";
		int firstResult = ( pager.getPageNumber() - 1 ) * pager.getPageSize();
		
		List<T> result = this.getSession().createQuery( hql )
			.setFirstResult( firstResult )
			.setMaxResults( pager.getPageSize() )
			.list();
		
		pager.setResult( result );
		
		return pager;
	}

	@Override
	public Pager findPager(Pager pager, Criterion... criterions) {
		int firstResult = ( pager.getPageNumber() - 1 ) * pager.getPageSize();
		
		List<T> result = null;
		Criteria criteria = this.getSession()
			.createCriteria( entityClass );
		
		for ( Criterion criterion : criterions ) {
			criteria.add( criterion );
		}
		
		
//		List<T> result = this.getSession()
//			.createCriteria( entityClass )
//			.add( criterions )
//			.setFirstResult( firstResult )
//			.setMaxResults( pager.getPageSize() )
//			.list();
		result = criteria.setFirstResult( firstResult )
			.setMaxResults( pager.getPageSize() )
			.list();
	
		
		pager.setResult( result );
		pager.setTotalCount( criteriaResultTotalCount( criteria ) );
		
		return pager;
	}

	private int criteriaResultTotalCount(Criteria criteria) {
		int totalCount = ( (Long) criteria.setProjection( Projections.rowCount() ).uniqueResult() ).intValue();
		
		return totalCount;
	}

	
	/*@Override
	public void updateInOneTransaction(PK pk) {
		T t = this.get( pk );
		
	}*/

}
